<?php
/**
 * lsys orm
 * @author     Lonely <shan.liu@msn.com>
 * @copyright  (c) 2017 Lonely <shan.liu@msn.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0
 */
namespace LSYS\ORMCache;
use LSYS\ORM;
use LSYS\Config;
use LSYS\ORM\Exception;
use LSYS\ORM\Entity;
use LSYS\ConfigFinder;
abstract class Cache implements ConfigFinder {
	use \LSYS\ConfigFinder\ConfigShare;
	/**
	 *
	 * @var string default instance name
	 */
	public static $config = 'orm.cache';
	/**
	 *
	 * @var array Database instances
	 */
	protected static $instances = array ();
	/**
	 * 得到数据库连接单例
	 *
	 * @param Config $name
	 * @return Cache
	 */
	public static function instance(Config $config=null) {
		if ($config === NULL){
			if (is_string(self::$config)){
				self::$config = self::find_config(self::$config);
			}
			$config=self::$config;
		}
		$name=$config->name();
		if (! isset ( self::$instances [$name] )) {
			$driver=$config->get("driver",NULL);
			if (!class_exists($driver,true)||!in_array('LSYS\ORM\Cache',class_parents($driver))){
				throw new Exception( __('ORM Cache driver not defined in [:name on :driver] configuration',array("name"=>$name,"driver"=>$driver)));
			}
			$driver = new $driver ( $config );
			self::$instances [$name] = $driver;
		}
		return self::$instances [$name];
	}
	//cache time
	private $_cache_time;
	/**
	 * @param Config $config
	 */
	public function __construct(Config $config){
		$this->_cache_time=$config->get("time");
	}
	/**
	 * set entity cache time
	 * @param int $cache_time
	 * @return number|\LSYS\ORM\Cache
	 */
	public function set_time($cache_time=null){
		if ($cache_time===null)return $this->_cache_time;
		$this->_cache_time=$cache_time<=0?$this->_cache_time:intval($cache_time);
		return $this;
	}
	protected function _pk_key($class,$pk){
		return $class.'_'.$pk;
	}
	protected function _where_pk($class,$where){
		return $class.'_'.md5(trim(json_encode($where)));
	}
	/**
	 * set entity to cache
	 * @param Entity $entity
	 * @return bool
	 */
	public function set_entity(Entity $entity){
		$data=array();
		foreach ($entity->columns() as $var)
		{
			$data[$var] = $entity->{$var};
		}
		$pk=$this->_pk_key(get_class($entity),$entity->pk());
		return $this->_set_orm($pk, $data, $this->_cache_time);
	}
	/**
	 * check entity object or entity pk exits in cache
	 * @param mixed $entity
	 * @return bool
	 */
	public function exist_entity($entity){
		if ($entity instanceof Entity) $pk= $entity->pk();
		return $this->pk_exist_entity(get_class($entity),$pk);
	}
	/**
	 * delete entity from cache
	 * @param mixed $entity
	 * @return boolean
	 */
	public function del_entity($entity){
		if ($entity instanceof Entity) $pk= $entity->pk();
		return boolval($this->pk_del_entity(get_class($entity),$pk));
	}
	/**
	 * delete entity from cache by pk
	 * @param string $class
	 * @param string $pk
	 * @return bool
	 */
	public function pk_del_entity($class,$pk){
		$pk=$this->_pk_key($class,strval($pk));
		return $this->_del_orm($pk);
	}
	/**
	 * check entity in cache by pk
	 * @param string $class
	 * @param string $pk
	 * @return bool
	 */
	public function pk_exist_entity($class,$pk){
		$pk=$this->_pk_key($class,$pk);
		return $this->_exist_orm($pk);
	}
	/**
	 * get entity by 
	 * @param ORM $orm
	 * @param string $pk
	 * @return \LSYS\ORM\Entity
	 */
	public function pk_get_entity(ORM $orm,$pk){
		$spk=$this->_pk_key(get_class($orm->entity()),$pk);
		return $this->_pk_get_entity($orm, $spk);
	}
	protected function _pk_get_entity(ORM $orm,$spk){
		$data=$this->_get_orm($spk);
		$pkk=$orm->primary_key();
		if (!is_array($data)||!isset($data[$pkk])) return false;
		$entity=$orm->entity();
		$entity->values($data);
		$entity->{$pkk}=$data[$pkk];
		return $entity;
	}
	/**
	 * proxy orm find method
	 * @param ORM $orm
	 * @return \LSYS\ORM
	 */
	public function find(ORM $orm){
		$db_pending=$orm->db_pending();
		foreach ( $db_pending as $k=>$method ) {
			switch ($method ['name']) {
				case 'limit' :
					unset($db_pending[$k]);
				break;
			}
		}
		ksort($db_pending);
		sort($db_pending);
		$class=get_class($orm->entity());
		$pk=$this->_get_where($this->_where_pk($class,$db_pending));
		if ($pk!==false){
			$entity=$this->_pk_get_entity($orm, $pk);
		}
		if (isset($entity)&&$entity&&$entity->loaded()){
			return $orm->entity($entity)->reset();
		}
		$orm->find();
		if ($orm->entity()->loaded()){
			if($this->set_entity($orm->entity())){
				$this->_set_where($this->_where_pk($class,$db_pending), $this->_pk_key($class,$orm->entity()->pk()), $this->_cache_time);
			}
		}
		return $orm;
	}
	abstract protected function _set_where($pk,$data,$timeout);
	abstract protected function _get_where($pk);
	abstract protected function _set_orm($pk,array $data,$timeout);
	abstract protected function _get_orm($pk);
	abstract protected function _exist_orm($pk);
	abstract protected function _del_orm($pk);
}